#include "perspectiveviewwidget.h"
#include "ui_perspectiveviewwidget.h"
#include "ActiveMILUtils.h"
#include "../share/studyrecord.h"
#include "../share/global.h"
#include "../MainStation/mainwindow.h"

#include <QUuid>
#include <QAxObject>
#include <QAxWidget>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QResizeEvent>
#include <QSettings>
#include <QDebug>

#define BUFFERING_SIZE_MAX 20
#define MAX_FRAME_COUNT 150
#define WINDOW_LOW "WINDOWLOW"
#define WINDOW_HIGH "WINDOWHIGH"
#define WINDOW_MAX "WINDOWMAX"
#define SAVE_FRAMES "SAVEFRAMES"
#define IBS_ENABLE "IBSENABLE"
#define IMAGE_ENHANCE "IMAGEENHANCE"
#define DENOISE_LEVEL "DENOISELEVEL"
#define IBS_CENTER "IBSCENTER"

void __stdcall _com_raise_error(HRESULT hr, IErrorInfo* perrinfo)
{
    QString exception;

    BSTR bstrSource;
    perrinfo->GetSource(&bstrSource);
    if (bstrSource != NULL)
        {
        //pException->m_strSource = bstrSource;
        SysFreeString(bstrSource);
        }

    BSTR bstrDescription;
    perrinfo->GetDescription(&bstrDescription);
    if (bstrDescription != NULL)
        {
        exception = QString::fromWCharArray(bstrDescription);
        SysFreeString(bstrDescription);
        }

    BSTR bstrHelpFile;
    perrinfo->GetHelpFile(&bstrHelpFile);
    if (bstrHelpFile != NULL)
        {
        //pException->m_strHelpFile = bstrHelpFile;
        SysFreeString(bstrHelpFile);
        }

    //perrinfo->GetHelpContext(&pException->m_dwHelpContext);
    //pException->m_scError = hr;

    // then throw the exception
    throw exception;
}

PerspectiveViewWidget::PerspectiveViewWidget(QWidget *parent) :
    numOfFrames(0),
    numOfSavingFrames(0),
    saveLater(false),
    delay(0),
    QDialog(parent),
    ui(new Ui::PerspectiveViewWidget)
{
    ui->setupUi(this);
    init();
}

PerspectiveViewWidget::~PerspectiveViewWidget()
{
    QSettings s;
    s.setValue(WINDOW_LOW, ui->lowSlider->value());
    s.setValue(WINDOW_MAX, ui->maxSlider->value());
    s.setValue(WINDOW_HIGH, ui->highSlider->value());
    s.setValue(SAVE_FRAMES, ui->saveFramesCheck->isChecked());
    s.setValue(IBS_ENABLE, ui->ibsCheck->isChecked());
    s.setValue(IMAGE_ENHANCE, ui->imageEnhanceCheck->isChecked());
    s.setValue(DENOISE_LEVEL, ui->smoothFactorSpin->value());
    s.setValue(IBS_CENTER, ui->ibsCenterSpin->value());

    pixelData.clear();
    delete ui;
}

void PerspectiveViewWidget::init()
{
    IApplication *app;
    IDisplay *dis;
    IDigitizer *dig;
    ui->axAppWidget->queryInterface(__uuidof(IApplication), (void**)&app);
    ui->axDisWidget->queryInterface(__uuidof(IDisplay), (void**)&dis);
    ui->axDigWidget->queryInterface(__uuidof(IDigitizer), (void**)&dig);
    pApp.Attach(app, false);
    pDis.Attach(dis, false);
    pDig.Attach(dig, false);

    ui->axAppWidget->setVisible(false);
    ui->axDigWidget->setVisible(false);

    try {
        pSys = pApp->CreateObject("MIL.System", false);
        pImgPro = pApp->CreateObject("MIL.ImageProcessing", false);
        pImgDest = pApp->CreateObject("MIL.Image", false);
        pGC = pApp->CreateObject("MIL.GraphicContext", false);
        pImgSum = pApp->CreateObject("MIL.Image", false);
    } catch (QString &e) {
        QMessageBox::critical(this, tr("Exception Caught"), e);
        const_cast<MainWindow*>(mainWindow)->close();
        return;
    }

    pImgSum->OwnerSystem = pSys;
    pImgDest->OwnerSystem = pSys;
    pImgPro->OwnerSystem = pSys;
    pDig->OwnerSystem = pSys;
    pDis->OwnerSystem = pSys;
    pGC->OwnerSystem = pSys;

    pSys->Allocate();
    pDig->Allocate();
    pGC->Allocate();
    pDis->Allocate();
    pImgPro->Allocate();

    pImgDest->CanGrab = false;
    pImgDest->CanProcess = true;
    pImgDest->CanDisplay = true;
    pImgDest->NumberOfBands = pDig->NumberOfBands;
    pImgDest->SizeX = (long)(pDig->SizeX);
    pImgDest->SizeY = (long)(pDig->SizeY);

    pImgSum->CanGrab = false;
    pImgSum->CanProcess = true;
    pImgSum->CanDisplay = false;
    pImgSum->NumberOfBands = pDig->NumberOfBands;
    pImgSum->SizeX = (long)(pDig->SizeX);
    pImgSum->SizeY = (long)(pDig->SizeY);
    pImgSum->DataType = imUnsigned;
    pImgSum->DataDepth = 16;

    pImgDest->Allocate();
    pImgSum->Allocate();

    pDis->Image = pImgDest;
    pGC->Image = pImgDest;

    int depth = pDig->DataDepth;
    int imageMax = (1<<depth)-1;
    pDis->LUT->GenerateRamp(0, 0, imageMax, 255);
    pDis->LUTEnabled = true;

    while(BUFFERING_SIZE_MAX > images.size()) {
        try {
            IImagePtr NewImage = pApp->CreateObject("MIL.Image", FALSE);

            if(NewImage != NULL) {
                NewImage->CanGrab = TRUE;
                NewImage->CanDisplay = FALSE;
                NewImage->CanProcess = TRUE;
                NewImage->NumberOfBands = pDig->NumberOfBands;
                NewImage->SizeX = (long)(pDig->SizeX/* * pDig->ScaleX*/);
                NewImage->SizeY = (long)(pDig->SizeY/* * pDig->ScaleY*/);
                NewImage->Allocate();

                // Add the image to the array of images
                //
                images.append(NewImage);

            }
        } catch (QString &e) {
            QMessageBox::critical(this, tr("Exception Caught"), e);
            exit(-1);
        }
    }

    //ui->horizontalLayout->insertWidget(0, axDis, 1);
    QSettings s;
    ui->lowSlider->setValue(s.value(WINDOW_LOW, 0).toInt());
    ui->highSlider->setValue(s.value(WINDOW_HIGH, 255).toInt());
    ui->maxSlider->setValue(s.value(WINDOW_MAX, 255).toInt());
    ui->saveFramesCheck->setChecked(s.value(SAVE_FRAMES, false).toBool());
    ui->ibsCheck->setChecked(s.value(IBS_ENABLE, true).toBool());
    ui->imageEnhanceCheck->setChecked(s.value(IMAGE_ENHANCE, false).toBool());
    ui->smoothFactorSpin->setValue(s.value(DENOISE_LEVEL, 50).toInt());
    ui->ibsCenterSpin->setValue(s.value(IBS_CENTER, 128).toInt());

    ui->smoothFactorSpin->setMaximum(18);

    setupConnections();
}

void PerspectiveViewWidget::setupConnections()
{
    connect(ui->maxSlider, SIGNAL(valueChanged(int)), this, SLOT(generateLutRamps()));
    connect(ui->lowSlider, SIGNAL(valueChanged(int)), this, SLOT(generateLutRamps()));
    connect(ui->highSlider, SIGNAL(valueChanged(int)), this, SLOT(generateLutRamps()));
    connect(ui->startButton, SIGNAL(clicked(bool)), this, SLOT(startView(bool)));

    connect(ui->axDigWidget, SIGNAL(ProcessModifiedImage(int)), this, SLOT(onProcessModifiedImage(int)));
    connect(ui->axDigWidget, SIGNAL(exception(int, QString, QString, QString)), this, SLOT(onException(int,QString,QString,QString)));
}

void PerspectiveViewWidget::onException(int code, const QString &source, const QString &desc, const QString &help)
{
    QMessageBox::critical(this, tr("Exception Occurred"), tr("Code: %1\nSource: %2\nDescription: %3\nHelp: %4")
                          .arg(QString::number(code), source, desc, help));
}

void PerspectiveViewWidget::onMultiFrameSaved(bool /*ok*/)
{
    int width = pImgDest->SizeX;
    int height = pImgDest->SizeY;

    pixelData.remove(0, numOfSavingFrames*width*height);
    numOfSavingFrames = 0;

    if (saveLater) {
        saveLater = false;
        saveMultiFrames();
    }
}

void PerspectiveViewWidget::onProcessModifiedImage(int index)
{
    IImagePtr curImage = images[index];
    int count = ui->smoothFactorSpin->value()+1;

    try {
        pDis->UpdateEnabled = false;

        pImgPro->Source1 = _variant_t(curImage, true);
        pImgPro->Source2 = _variant_t(pImgSum, true);
        pImgPro->Destination1 = _variant_t(pImgSum, true);
        pImgPro->Add(true);

        avgImages.prepend(curImage);
        while (avgImages.size() > count) {
            pImgPro->Source1 = _variant_t(pImgSum, true);
            pImgPro->Source2 = _variant_t(avgImages.last(), true);
            pImgPro->Destination1 = _variant_t(pImgSum, true);
            pImgPro->Subtract(false, true);
            avgImages.removeLast();
        }

        pImgPro->Source1 = _variant_t(pImgSum, true);
        pImgPro->Source2 = avgImages.size();
        pImgPro->Destination1 = _variant_t(pImgDest, true);
        pImgPro->Divide(false);

        pImgPro->Source1 = _variant_t(pImgDest, true);
        pImgPro->Destination1 = _variant_t(pImgDest, true);
        if (ui->imageEnhanceCheck->isChecked()) pImgPro->Sharpen2(impOverscanEnable);
        pImgPro->Flip(impHorizontal);

        //pImgPro->Source1 = pImgDest;
        //pImgPro->Rank(imp3x3Rect, impMedian, impGrayscale);
        //pImgPro->Smooth(impOverscanDisable);
        //pImgPro->Shen(impSmooth, ui->smoothFactorSpin->value());
        //pImgPro->Sharpen1(impOverscanEnable);

        pGC->Text(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz").toLocal8Bit().data());
        pDis->UpdateEnabled = true;
    } catch (QString &e) {
        const_cast<MainWindow*>(mainWindow)->setStatusTip(e);
        //QMessageBox::critical(this, tr("Exception Caught"), e);
    }

    if (delay > 0) delay--;
    if (ui->ibsCheck->isChecked() && delay == 0) {
        const uchar *pixel = (uchar*)curImage->HostAddress;
        int width = curImage->SizeX;
        int height = curImage->SizeY;

        int gridX = width/7;
        int gridY = height/7;
        long grayTotal = 0;
        long pixelTotal = 0;
        for (int y = gridY*3; y < gridY*4; ++y) {
            for (int x = gridX*3; x < gridX*4; ++x) {
                grayTotal += *(pixel+(y*pImgDest->PitchByte+x));
                pixelTotal++;
            }
        }
        long level = grayTotal/pixelTotal;
        int center = ui->ibsCenterSpin->value();

        if (level > center+10) {
            emit decKv();
            delay = 2;
        } else if (level < center-12) {
            emit incKv();
            delay = 2;
        }

        ui->avgPixelValueLabel->setText(QString::number(level));
    }

    if (ui->saveFramesCheck->isChecked() && (avgImages.size()==count)) {
        const char *pixel = (char*)pImgDest->HostAddress;
        int width = pImgDest->SizeX;
        int height = pImgDest->SizeY;
        for (int i = 0; i < height; ++i) {
            pixelData.append(pixel+(i*pImgDest->PitchByte), width);
        }
        numOfFrames++;
    }

    if (numOfFrames >= MAX_FRAME_COUNT) {
        saveMultiFrames();
    }
}

void PerspectiveViewWidget::saveMultiFrames()
{
    if (numOfSavingFrames) {
        saveLater = true;
    } else {
        numOfSavingFrames = (numOfFrames<MAX_FRAME_COUNT)?numOfFrames:(MAX_FRAME_COUNT);
        numOfFrames -= numOfSavingFrames;
        int width = pImgDest->SizeX;
        int height = pImgDest->SizeY;
        double winWidth = ui->highSlider->value() - ui->lowSlider->value();
        double winCenter = ui->lowSlider->value() + winWidth/2;
        emit savePerspectImage(pixelData, numOfSavingFrames, width, height, winCenter, winWidth);
    }
}

void PerspectiveViewWidget::startView(bool yes)
{
    if (yes) {
        // Build a CActiveMILObjectsArray
        //
        CActiveMILObjectsArray<IImagePtr> ActiveMILObjectsArray((void**)images.data(), images.size());

        // Start the processing.  The processing event is fired for every frame grabbed
        //
        try {
            //Clear averaging images
            avgImages.clear();
            pImgSum->Clear(0);
            pDig->MultipleBuffering->Process(digStart, ActiveMILObjectsArray);
        } catch (QString &e) {
            const_cast<MainWindow*>(mainWindow)->setStatusTip(e);
            //QMessageBox::critical(this, tr("Exception Caught"), e);
        }
    } else {
        try {
            pDig->MultipleBuffering->Halt(false);
        } catch (QString &e) {
            const_cast<MainWindow*>(mainWindow)->setStatusTip(e);
            //QMessageBox::critical(this, tr("Exception Caught"), e);
        }

        if (numOfFrames > 0) {
            saveMultiFrames();
        }
        /*
        QMessageBox::information(this, tr("Statistics"),
                                 tr("Frame Rate: %1, Frame Processed: %2, Frame Missed: %3.")
                                 .arg(pDig->MultipleBuffering->FrameRate)
                                 .arg(pDig->MultipleBuffering->NumberOfFrameProcessed)
                                 .arg(pDig->MultipleBuffering->NumberOfFrameMissed));
                                 */
    }
}

void PerspectiveViewWidget::generateLutRamps()
{
    pDis->UpdateEnabled = false;

    int imageMax = (1<<(pDig->DataDepth))-1;
    int low = ui->lowSlider->value();
    int high = ui->highSlider->value();
    if (low > high) {
        if (sender() == ui->lowSlider) {
            ui->highSlider->setValue(low);
            high = low;
        } else {
            ui->lowSlider->setValue(high);
            low = high;
        }
    }
    int max = ui->maxSlider->value();

    try {
        pDis->LUT->GenerateRamp(0, 0, low, 0);
        pDis->LUT->GenerateRamp(low, 0, high, max);
        pDis->LUT->GenerateRamp(high, max, imageMax, 255);
    } catch (QString &e) {
        QMessageBox::critical(this, tr("Exception Caught"), e);
    }

    pDis->UpdateEnabled = true;

    QSettings s;
    s.setValue(WINDOW_LOW, ui->lowSlider->value());
    s.setValue(WINDOW_HIGH, ui->highSlider->value());
    s.setValue(WINDOW_MAX, ui->maxSlider->value());
    s.setValue(SAVE_FRAMES, ui->saveFramesCheck->isChecked());
}

void PerspectiveViewWidget::onZoom(double factor)
{
    pDis->Zoom(factor, factor, false);
}

void PerspectiveViewWidget::markFrame()
{

}

void PerspectiveViewWidget::resizeEvent(QResizeEvent *e)
{
    QWidget::resizeEvent(e);

    QSize size = ui->axDisWidget->size();
    int width = pDig->SizeX;
    int height = pDig->SizeY;
    pDis->PanX = (width-size.width())/2;
    pDis->PanY = (height-size.height())/2;
}
